#pragma once

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log.hpp"
enum
{
    SocketErr = 2,
    BindErr = 3,
    ListenErr = 4,
};

const int backlog = 10;
class Socket
{
public:
    Socket()
    {
    }
    ~Socket()
    {
    }
    void CreateSocket()
    {
        _socketfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_socketfd < 0)
        {
            l(Fatal, "create socket fail,%s: %d", strerror(errno), errno);
            exit(SocketErr);
        }
        int opt = 1;
        setsockopt(_socketfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    }
    void Bind(uint16_t port)
    {
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        local.sin_addr.s_addr = INADDR_ANY;
        socklen_t len = sizeof(local);
        int n = bind(_socketfd, (struct sockaddr *)&local, len);

        if (n < 0)
        {
            l(Fatal, "bind fail,%s: %d", strerror(errno), errno);
            exit(BindErr);
        }
    }
    void Listen()
    {
        if (listen(_socketfd, backlog) < 0)
        {
            l(Fatal, "listen fail,%s: %d", strerror(errno), errno);
            exit(ListenErr);
        }
    }
    int Accept(std::string *clientip, uint16_t *port)
    {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        int newfd = accept(_socketfd, (struct sockaddr *)&client, &len);
        if (newfd < 0)
        {
            l(Warning, "accept fail,%s: %d", strerror(errno), errno);
            return -1;
        }
        char buff[64];
        inet_ntop(AF_INET, &client.sin_addr, buff, sizeof(buff));
        *clientip = buff;
        // *clientip = inet_ntoa(client.sin_addr);
        *port = ntohs(client.sin_port);

        return newfd;
    }
    bool Connect(const std::string &ip, uint16_t port)
    {
        struct sockaddr_in c;
        bzero(&c, sizeof(c));
        c.sin_family = AF_INET;
        c.sin_port = htons(port);
        // inet_pton(AF_INET, ip.c_str(), &c.sin_addr);
        c.sin_addr.s_addr = inet_addr(ip.c_str());
        socklen_t len = sizeof(c);
        int n = connect(_socketfd, (sockaddr *)&c, len);
        if (n == -1)  return false;
        return true;
    }
    void Close()
    {
        close(_socketfd);
    }

    int GetSocketfd()
    {
        return _socketfd;
    }

private:
    int _socketfd;
};